/*
 *  Version.cpp
 *  zxing
 *
 *  Created by Luiz Silva on 09/02/2010.
 *  Copyright 2010 ZXing authors All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <zxing/datamatrix/Version.h>
#include <limits>
#include <iostream>

namespace zxing {
    namespace datamatrix {
        using namespace std;

        ECB::ECB(int count, int dataCodewords) :
                count_(count), dataCodewords_(dataCodewords) {
        }

        int ECB::getCount() {
            return count_;
        }

        int ECB::getDataCodewords() {
            return dataCodewords_;
        }

        ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks) :
                ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks) {
        }

        ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2) :
                ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks1) {
            ecBlocks_.push_back(ecBlocks2);
        }

        int ECBlocks::getECCodewords() {
            return ecCodewords_;
        }

        std::vector<ECB *> &ECBlocks::getECBlocks() {
            return ecBlocks_;
        }

        ECBlocks::~ECBlocks() {
            for (size_t i = 0; i < ecBlocks_.size(); i++) {
                delete ecBlocks_[i];
            }
        }

        vector<Ref<Version> > Version::VERSIONS;
        static int N_VERSIONS = Version::buildVersions();

        Version::Version(int versionNumber, int symbolSizeRows, int symbolSizeColumns,
                         int dataRegionSizeRows,
                         int dataRegionSizeColumns, ECBlocks *ecBlocks) : versionNumber_(
                versionNumber),
                                                                          symbolSizeRows_(
                                                                                  symbolSizeRows),
                                                                          symbolSizeColumns_(
                                                                                  symbolSizeColumns),
                                                                          dataRegionSizeRows_(
                                                                                  dataRegionSizeRows),
                                                                          dataRegionSizeColumns_(
                                                                                  dataRegionSizeColumns),
                                                                          ecBlocks_(ecBlocks),
                                                                          totalCodewords_(0) {
            // Calculate the total number of codewords
            int total = 0;
            int ecCodewords = ecBlocks_->getECCodewords();
            vector<ECB *> &ecbArray = ecBlocks_->getECBlocks();
            for (unsigned int i = 0; i < ecbArray.size(); i++) {
                ECB *ecBlock = ecbArray[i];
                total += ecBlock->getCount() * (ecBlock->getDataCodewords() + ecCodewords);
            }
            totalCodewords_ = total;
        }

        Version::~Version() {
            delete ecBlocks_;
        }

        int Version::getVersionNumber() {
            return versionNumber_;
        }

        int Version::getSymbolSizeRows() {
            return symbolSizeRows_;
        }

        int Version::getSymbolSizeColumns() {
            return symbolSizeColumns_;
        }

        int Version::getDataRegionSizeRows() {
            return dataRegionSizeRows_;
        }

        int Version::getDataRegionSizeColumns() {
            return dataRegionSizeColumns_;
        }

        int Version::getTotalCodewords() {
            return totalCodewords_;
        }

        ECBlocks *Version::getECBlocks() {
            return ecBlocks_;
        }

        Ref<Version> Version::getVersionForDimensions(int numRows, int numColumns) {
            if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
                throw ReaderException("Number of rows and columns must be even");
            }

            // TODO(bbrown): This is doing a linear search through the array of versions.
            // If we interleave the rectangular versions with the square versions we could
            // do a binary search.
            for (int i = 0; i < N_VERSIONS; ++i) {
                Ref<Version> version(VERSIONS[i]);
                if (version->getSymbolSizeRows() == numRows &&
                    version->getSymbolSizeColumns() == numColumns) {
                    return version;
                }
            }
            throw ReaderException("Error version not found");
        }

/**
 * See ISO 16022:2006 5.5.1 Table 7
 */
        int Version::buildVersions() {
            VERSIONS.push_back(Ref<Version>(new Version(1, 10, 10, 8, 8,
                                                        new ECBlocks(5, new ECB(1, 3)))));
            VERSIONS.push_back(Ref<Version>(new Version(2, 12, 12, 10, 10,
                                                        new ECBlocks(7, new ECB(1, 5)))));
            VERSIONS.push_back(Ref<Version>(new Version(3, 14, 14, 12, 12,
                                                        new ECBlocks(10, new ECB(1, 8)))));
            VERSIONS.push_back(Ref<Version>(new Version(4, 16, 16, 14, 14,
                                                        new ECBlocks(12, new ECB(1, 12)))));
            VERSIONS.push_back(Ref<Version>(new Version(5, 18, 18, 16, 16,
                                                        new ECBlocks(14, new ECB(1, 18)))));
            VERSIONS.push_back(Ref<Version>(new Version(6, 20, 20, 18, 18,
                                                        new ECBlocks(18, new ECB(1, 22)))));
            VERSIONS.push_back(Ref<Version>(new Version(7, 22, 22, 20, 20,
                                                        new ECBlocks(20, new ECB(1, 30)))));
            VERSIONS.push_back(Ref<Version>(new Version(8, 24, 24, 22, 22,
                                                        new ECBlocks(24, new ECB(1, 36)))));
            VERSIONS.push_back(Ref<Version>(new Version(9, 26, 26, 24, 24,
                                                        new ECBlocks(28, new ECB(1, 44)))));
            VERSIONS.push_back(Ref<Version>(new Version(10, 32, 32, 14, 14,
                                                        new ECBlocks(36, new ECB(1, 62)))));
            VERSIONS.push_back(Ref<Version>(new Version(11, 36, 36, 16, 16,
                                                        new ECBlocks(42, new ECB(1, 86)))));
            VERSIONS.push_back(Ref<Version>(new Version(12, 40, 40, 18, 18,
                                                        new ECBlocks(48, new ECB(1, 114)))));
            VERSIONS.push_back(Ref<Version>(new Version(13, 44, 44, 20, 20,
                                                        new ECBlocks(56, new ECB(1, 144)))));
            VERSIONS.push_back(Ref<Version>(new Version(14, 48, 48, 22, 22,
                                                        new ECBlocks(68, new ECB(1, 174)))));
            VERSIONS.push_back(Ref<Version>(new Version(15, 52, 52, 24, 24,
                                                        new ECBlocks(42, new ECB(2, 102)))));
            VERSIONS.push_back(Ref<Version>(new Version(16, 64, 64, 14, 14,
                                                        new ECBlocks(56, new ECB(2, 140)))));
            VERSIONS.push_back(Ref<Version>(new Version(17, 72, 72, 16, 16,
                                                        new ECBlocks(36, new ECB(4, 92)))));
            VERSIONS.push_back(Ref<Version>(new Version(18, 80, 80, 18, 18,
                                                        new ECBlocks(48, new ECB(4, 114)))));
            VERSIONS.push_back(Ref<Version>(new Version(19, 88, 88, 20, 20,
                                                        new ECBlocks(56, new ECB(4, 144)))));
            VERSIONS.push_back(Ref<Version>(new Version(20, 96, 96, 22, 22,
                                                        new ECBlocks(68, new ECB(4, 174)))));
            VERSIONS.push_back(Ref<Version>(new Version(21, 104, 104, 24, 24,
                                                        new ECBlocks(56, new ECB(6, 136)))));
            VERSIONS.push_back(Ref<Version>(new Version(22, 120, 120, 18, 18,
                                                        new ECBlocks(68, new ECB(6, 175)))));
            VERSIONS.push_back(Ref<Version>(new Version(23, 132, 132, 20, 20,
                                                        new ECBlocks(62, new ECB(8, 163)))));
            VERSIONS.push_back(Ref<Version>(new Version(24, 144, 144, 22, 22,
                                                        new ECBlocks(62, new ECB(8, 156),
                                                                     new ECB(2, 155)))));
            VERSIONS.push_back(Ref<Version>(new Version(25, 8, 18, 6, 16,
                                                        new ECBlocks(7, new ECB(1, 5)))));
            VERSIONS.push_back(Ref<Version>(new Version(26, 8, 32, 6, 14,
                                                        new ECBlocks(11, new ECB(1, 10)))));
            VERSIONS.push_back(Ref<Version>(new Version(27, 12, 26, 10, 24,
                                                        new ECBlocks(14, new ECB(1, 16)))));
            VERSIONS.push_back(Ref<Version>(new Version(28, 12, 36, 10, 16,
                                                        new ECBlocks(18, new ECB(1, 22)))));
            VERSIONS.push_back(Ref<Version>(new Version(29, 16, 36, 14, 16,
                                                        new ECBlocks(24, new ECB(1, 32)))));
            VERSIONS.push_back(Ref<Version>(new Version(30, 16, 48, 14, 22,
                                                        new ECBlocks(28, new ECB(1, 49)))));
            return VERSIONS.size();
        }
    }
}
